xend: Improve "cpus" parameter to be able to define CPU affinities for each VCPU
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 8 May 2008 10:15:58 +0000 (11:15 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 8 May 2008 10:15:58 +0000 (11:15 +0100)
If we define the form of list of string to "cpus" parameter,
different CPU affinities are set for each VCPU as follows.

 # grep cpus /etc/xen/vm1
 cpus = ["2", "3"]
 vcpus = 2
 # xm create vm1
 Using config file "/etc/xen/vm1".
 Started domain vm1
 # xm vcpu-list vm1
 Name                                ID  VCPU   CPU State   Time(s)
 CPU Affinity
 vm1                                  1     0     2   r--       3.5 2
 vm1                                  1     1     3   -b-       3.2 3

If we define the form of string to "cpus" parameter as before,
a same CPU affinity is set for each VCPU as follows.

 # grep cpus /etc/xen/vm2
 cpus = "2,3"
 vcpus = 2
 # xm create vm2
 Using config file "/etc/xen/vm2".
 Started domain vm2
 # xm vcpu-list vm2
 Name                                ID  VCPU   CPU State   Time(s)
 CPU Affinity
 vm2                                  2     0     3   -b-       3.0
 2-3
 vm2                                  2     1     2   r--       2.6
 2-3

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
tools/python/xen/xend/XendConfig.py
tools/python/xen/xend/XendDomain.py
tools/python/xen/xend/XendDomainInfo.py

index 8a4deacf572ebe0c294125909fe8a1b5cab97843..cdb4c1c6f381d27974810ffc245e534783c0997c 100644 (file)
@@ -640,46 +640,84 @@ class XendConfig(dict):
             else:
                 cfg['cpus'] = str(cfg['cpu'])
 
-        # Convert 'cpus' to list of ints
+        # Convert 'cpus' to list of list of ints
+        cpus_list = []
         if 'cpus' in cfg:
-            cpus = []
+            # Convert the following string to list of ints.
+            # The string supports a list of ranges (0-3),
+            # seperated by commas, and negation (^1).  
+            # Precedence is settled by order of the string:
+            #    "0-3,^1"      -> [0,2,3]
+            #    "0-3,^1,1"    -> [0,1,2,3]
+            def cnv(s):
+                l = []
+                for c in s.split(','):
+                    if c.find('-') != -1:
+                        (x, y) = c.split('-')
+                        for i in range(int(x), int(y)+1):
+                            l.append(int(i))
+                    else:
+                        # remove this element from the list 
+                        if c[0] == '^':
+                            l = [x for x in l if x != int(c[1:])]
+                        else:
+                            l.append(int(c))
+                return l
+            
             if type(cfg['cpus']) == list:
-                # If sxp_cfg was created from config.sxp,
-                # the form of 'cpus' is list of string.
-                # Convert 'cpus' to list of ints.
-                #    ['1']         -> [1]
-                #    ['0','2','3'] -> [0,2,3]
-                try:
-                    for c in cfg['cpus']:
-                        cpus.append(int(c))
-                    
-                    cfg['cpus'] = cpus
-                except ValueError, e:
-                    raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
+                if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list:
+                    # If sxp_cfg was created from config.sxp,
+                    # the form of 'cpus' is list of list of string.
+                    # Convert 'cpus' to list of list of ints.
+                    # Conversion examples:
+                    #    [['1']]               -> [[1]]
+                    #    [['0','2'],['1','3']] -> [[0,2],[1,3]]
+                    try:
+                        for c1 in cfg['cpus']:
+                            cpus = []
+                            for c2 in c1:
+                                cpus.append(int(c2))
+                            cpus_list.append(cpus)
+                    except ValueError, e:
+                        raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
+                else:
+                    # Conversion examples:
+                    #    ["1"]               -> [[1]]
+                    #    ["0,2","1,3"]       -> [[0,2],[1,3]]
+                    #    ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]]
+                    try:
+                        for c in cfg['cpus']:
+                            cpus = cnv(c)
+                            cpus_list.append(cpus)
+                    except ValueError, e:
+                        raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
+                
+                if len(cpus_list) != cfg['vcpus']:
+                    raise XendConfigError('vcpus and the item number of cpus are not same')
             else:
-                # Convert 'cpus' string to list of ints
-                # 'cpus' supports a list of ranges (0-3),
-                # seperated by commas, and negation, (^1).  
-                # Precedence is settled by order of the 
-                # string:
-                #    "0-3,^1"      -> [0,2,3]
-                #    "0-3,^1,1"    -> [0,1,2,3]
+                # Conversion examples:
+                #  vcpus=1:
+                #    "1"      -> [[1]]
+                #    "0-3,^1" -> [[0,2,3]]
+                #  vcpus=2:
+                #    "1"      -> [[1],[1]]
+                #    "0-3,^1" -> [[0,2,3],[0,2,3]]
                 try:
-                    for c in cfg['cpus'].split(','):
-                        if c.find('-') != -1:             
-                            (x, y) = c.split('-')
-                            for i in range(int(x), int(y)+1):
-                                cpus.append(int(i))
-                        else:
-                            # remove this element from the list 
-                            if c[0] == '^':
-                                cpus = [x for x in cpus if x != int(c[1:])]
-                            else:
-                                cpus.append(int(c))
-                    
-                    cfg['cpus'] = cpus
+                    cpus = cnv(cfg['cpus'])
+                    for v in range(0, cfg['vcpus']):
+                        cpus_list.append(cpus)
                 except ValueError, e:
                     raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
+        else:
+            # Generation examples:
+            #  vcpus=1:
+            #    -> [[]]
+            #  vcpus=2:
+            #    -> [[],[]]
+            for v in range(0, cfg['vcpus']):
+                cpus_list.append(list())
+        
+        cfg['cpus'] = cpus_list
 
         # Parse cpuid
         if 'cpuid' in cfg:
index 6e93795675022ff2eea9c84e1f67c749d48c6f1f..39e49d13ac174ab25b3347fe3749f6b38b1a99f5 100644 (file)
@@ -1365,28 +1365,25 @@ class XendDomain:
             raise XendInvalidDomain(str(domid))
 
         # if vcpu is keyword 'all', apply the cpumap to all vcpus
-        vcpus = [ vcpu ]
         if str(vcpu).lower() == "all":
             vcpus = range(0, int(dominfo.getVCpuCount()))
+        else:
+            vcpus = [ int(vcpu) ]
        
         # set the same cpumask for all vcpus
         rc = 0
-        if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
-            for v in vcpus:
-                try:
-                    rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap)
-                except Exception, ex:
-                    log.exception(ex)
-                    raise XendError("Cannot pin vcpu: %s to cpu: %s - %s" % \
-                                    (v, cpumap, str(ex)))
-        else:
-            # FIXME: if we could define cpu affinity definitions to
-            #        each vcpu, reprogram the following processing.
-            if str(vcpu).lower() != "all":
-                raise XendError("Must specify 'all' to VCPU "
-                                "for inactive managed domains")
-            dominfo.setCpus(cpumap)
-            self.managed_config_save(dominfo)
+        cpus = dominfo.getCpus()
+        for v in vcpus:
+            try:
+                if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
+                    rc = xc.vcpu_setaffinity(dominfo.getDomid(), v, cpumap)
+                cpus[v] = cpumap
+            except Exception, ex:
+                log.exception(ex)
+                raise XendError("Cannot pin vcpu: %d to cpu: %s - %s" % \
+                                (v, cpumap, str(ex)))
+        dominfo.setCpus(cpus)
+        self.managed_config_save(dominfo)
 
         return rc
 
index f121cdb6fc3c98187eca714f02b99a809a6a18ac..607a97aef537d52f1a66e5a5c4558decb22c13e1 100644 (file)
@@ -1051,8 +1051,8 @@ class XendDomainInfo:
                                  ['running',  0],
                                  ['cpu_time', 0.0],
                                  ['cpu',      -1],
-                                 ['cpumap',   self.info['cpus'] and \
-                                              self.info['cpus'] or range(64)]])
+                                 ['cpumap',   self.info['cpus'][i] and \
+                                              self.info['cpus'][i] or range(64)]])
 
             return sxpr
 
@@ -1477,6 +1477,13 @@ class XendDomainInfo:
                 self.info['VCPUs_live'] = vcpus
                 self._writeDom(self._vcpuDomDetails())
         else:
+            if self.info['VCPUs_max'] > vcpus:
+                # decreasing
+                del self.info['cpus'][vcpus:]
+            elif self.info['VCPUs_max'] < vcpus:
+                # increasing
+                for c in range(self.info['VCPUs_max'], vcpus):
+                    self.info['cpus'].append(list())
             self.info['VCPUs_max'] = vcpus
             xen.xend.XendDomain.instance().managed_config_save(self)
         log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
@@ -2071,9 +2078,17 @@ class XendDomainInfo:
             # repin domain vcpus if a restricted cpus list is provided
             # this is done prior to memory allocation to aide in memory
             # distribution for NUMA systems.
-            if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
+            def has_cpus():
+                if self.info['cpus'] is not None:
+                    for c in self.info['cpus']:
+                        if c:
+                            return True
+                return False
+
+            if has_cpus():
                 for v in range(0, self.info['VCPUs_max']):
-                    xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
+                    if self.info['cpus'][v]:
+                        xc.vcpu_setaffinity(self.domid, v, self.info['cpus'][v])
             else:
                 def find_relaxed_node(node_list):
                     import sys